#pragma once
#include "common.hpp"
#include <memory>
#include "Socket.hpp"

#include <sys/time.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <unistd.h>


#define DEFAULT_BACKLOG 16 //最大同时listen连接数
#define DEFAULT_FD -1 //无效文件描述符
#define DEFAULT_FDS_SIZE 4096 //_fds初始长度
#define DEFAULT_EVENT_ARR_SIZE 64 //用户层接受数组大小

class epoll_server
{
private:
    // void print_fds()
    // {
    //     std::cout << "_fds: ";
    //     for(int i = 0; i < _nfds; ++i)
    //     {
    //         if(_fds[i].fd != DEFAULT_FD)
    //             std::cout << _fds[i].fd << " ";
    //     }
    //     std::cout << std::endl;//换行 + 刷新缓冲区
    // }


public:
    epoll_server(uint16_t port)
        :_listen_socket_ptr(std::make_unique<TCP_Socket>())
        ,_port(port)
        ,_running_tag(false)
        ,_epfd(DEFAULT_FD)
    {
        //1. 创建listen_socket_fd  -- 3
        _listen_socket_ptr->create_listen_socket(_port, DEFAULT_BACKLOG);

        //2. 创建并初始化eventpoll, 并将其关联到文件系统  -- 4
        _epfd = epoll_create(256);//size参数被忽略了, 随便传个大于0的数字即可
        if(_epfd < 0)
        {
            LOG(wzx::log_level::FATAL) << "epoll_create fail!!!";
            exit(EPOLL_CREATE_ERR);
        }

        //3. 初始化用户层数组 -- 用于接受epoll_wait()输出的就绪事件
        _event_arr = std::make_unique<struct epoll_event[]>(DEFAULT_EVENT_ARR_SIZE);

        //4. 让epoll监视listen_socket_fd上的事件
        struct epoll_event listen_event;
        listen_event.events = EPOLLIN;
        listen_event.data.fd = _listen_socket_ptr->get_socket_fd();//用户数据, 内核不做更改
        int ret_epoll_ctl = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listen_socket_ptr->get_socket_fd(), &listen_event);
        if(ret_epoll_ctl < 0)
        {
            LOG(wzx::log_level::FATAL) << "add listen_event fail!!!";
            exit(ADD_LISTEN_EVENT_FAIL);
        }

    }

    ~epoll_server()
    {
        LOG(wzx::log_level::DEBUG) << "epoll server close...";
        if(_epfd > 0)//_epfd没用智能指针管理, 需要手动close
            close(_epfd);
    }



    void start()
    {
        LOG(wzx::log_level::DEBUG) << "epoll server start...";
        _running_tag = true;
        while(_running_tag)
        {
            //debug
            // print_fds();

            //每次调用epoll_wait(), 内核都会将就绪事件从用户数组的0下标开始拷贝!!! 所以不用判断有效性, [0, ready_fd_count)均为有效事件
                                                   //get()接口: 返回智能指针的托管对象
            int ready_fd_count = epoll_wait(_epfd, _event_arr.get()/*输出型参数*/, 64/*自定义*/, 1000);

            if(ready_fd_count > 0) //有若干个fd的事件就绪了
            {
                LOG(wzx::log_level::INFO) << "several events are ready!!!  ready_fd_count: " << ready_fd_count;

                //交给事件派发器
                event_dispatcher(ready_fd_count);
                continue;
            }
            else if(ready_fd_count == 0) //超时了 -- 没有事件就绪
            {
                LOG(wzx::log_level::INFO) << "time out!!!";
                continue;
            }
            else //ready_fd_count < 0 -- epoll()出错了
            {
                LOG(wzx::log_level::FATAL) << "epoll error: " << errno;
                break;
            }

        }

        _running_tag = false;
    }


    //事件派发器 -- 我们这里只设计了读事件
    void event_dispatcher(int ready_fd_count)
    {
        for(int i = 0; i < ready_fd_count; ++i)
        {
            //就绪的读事件 -- 我这里只考虑读事件
            if(_event_arr[i].events & EPOLLIN)
            {
                //1. 新连接请求
                if(_event_arr[i].data.fd == _listen_socket_ptr->get_socket_fd())
                    connection_processer();

                //2. IO
                else
                    IO_processer(&_event_arr[i]);
                

            }

            //就绪的写事件
            // else if(_event_arr[i].events & EPOLLOUT)
            // {}
            // ...
        }
    }


    //连接处理器 -- accept
    void connection_processer()
    {
        //此时accept一定不会阻塞, 因为_listen_socket_fd上已经有事件就绪了
        Inet_Addr client;
        int socket_fd = _listen_socket_ptr->accept_socket(client);

        //不能直接用socket_fd进行IO, 否则又会变成阻塞IO了, 这里要将它托管给epoll -- epoll_ctl()
        struct epoll_event io_event;
        io_event.events = EPOLLIN;
        io_event.data.fd = socket_fd;
        int ret_epoll_ctl = epoll_ctl(_epfd, EPOLL_CTL_ADD, socket_fd, &io_event);
        
    }


    //IO处理器
    void IO_processer(struct epoll_event* p_io_event)
    {
        //此时recv一定不会阻塞, 因为socket_fd上已经有事件就绪了
        int socket_fd = p_io_event->data.fd;
        std::unique_ptr<Socket> socket_ptr = std::make_unique<TCP_Socket>(socket_fd);
        std::string message;//bug -- 这里读到的不一定是完整的 -- 但是我这里就简略一下 不进行完整报文处理了(>_<)
        bool ret_recv = socket_ptr->receive(message);
        if(ret_recv == true)
        {
            std::cout << "client say:> " << message << std::endl;
        }
        else //ret_recv == false; -- 对端关闭连接了
        {
            //epoll_ctl()只能移除合法fd -- 先移除, 再关闭！！
            //1. 从event_poll结构里移除io_event
            int ret_epoll_ctl = epoll_ctl(_epfd, EPOLL_CTL_DEL, socket_fd, p_io_event);

            //2. 关闭fd
            socket_ptr->close();
        }
    }



private:
    std::unique_ptr<Socket> _listen_socket_ptr;
    uint16_t _port;

    bool _running_tag;

    int _epfd; //指向eventpoll模型 -- Linux万物皆文件
    std::unique_ptr<struct epoll_event[]> _event_arr;//用户层数组, 用于接受epoll_wait()返回的就绪事件
};